﻿using System;
using System.Data.Linq;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Windows.System.Threading;
using Microsoft.Phone.BackgroundTransfer;
using Microsoft.Phone.Shell;
using Microsoft.WindowsAzure.MobileServices;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Media.PhoneExtensions;
using WP8DataModel.Model;
using WP8DataModel.ViewModel;

namespace CeskyRozhlasWP8.Model
{
    public static class BackgroundTransferManager
    {
        public async static Task RefreshAllTransfers()
        {
            if (Debugger.IsAttached)
            {
                await AddRandomItemForDownload();
            }
            using (var aDb = new BackgroundScheduleDataContext(BackgroundScheduleModel.DefaultDbConnectionString))
            {
                await RefreshAllTransfers(aDb);
            }

        }

        private static async Task RefreshAllTransfers(BackgroundScheduleDataContext iTargetDatabase)
        {
            if (!iTargetDatabase.DatabaseExists())
            {
                return;
            }

            var aDatabaseSubmitRequired = RemoveAllCompletedTransferRequests(iTargetDatabase);
            aDatabaseSubmitRequired |= await UpdatePendingTransferRequests(iTargetDatabase);
            if (aDatabaseSubmitRequired)
            {
                iTargetDatabase.SubmitChanges(ConflictMode.ContinueOnConflict);
            }
        }

        private static async Task<bool> UpdatePendingTransferRequests(BackgroundScheduleDataContext iTargetDatabase)
        {
            bool aDatabaseSubmitRequired = false;
            foreach (var iterShow in
                iTargetDatabase.BackgroundScheduleItems.
                    Where(request => request.StopTime <= DateTime.Now).
                    Where(request => !request.MovedToMediaLibrary).
                    Where(request => request.DownloadLater))
            {
                if (iterShow.BackgroundTransferRequestId == null)
                {
                    await AddItemToDownloadQueue(iterShow);
                    aDatabaseSubmitRequired = true;
                }
                else
                {
                    //If download manager have lost information about this download - just retry
                    if (BackgroundTransferService.Find(iterShow.BackgroundTransferRequestId) == null)
                    {
                        iterShow.BackgroundTransferRequestId = null;
                        iterShow.ErrorMessage = "Lost by Transfer Service;" + iterShow.ErrorMessage;
                        aDatabaseSubmitRequired = true;
                    }
                }
            }
            return aDatabaseSubmitRequired;
        }

        public static async Task AddRandomItemForDownload()
        {
            //var aShow = (await App._shows.ToEnumerableAsync()).FirstOrDefault();
            var aShow = await ShowsData.Instance.GetFirstShow();
            var aStation = await ShowsData.Instance.GetStationById(aShow.StationId);
            if (aShow != null && aStation != null)
            {
                using (
                    var aDb = new BackgroundScheduleDataContext(BackgroundScheduleModel.DefaultDbConnectionString))
                {
                    if (!aDb.DatabaseExists())
                    {
                        return;
                    }
                    if (aDb.BackgroundScheduleItems.Any(item => item.ShowId == aShow.Id))
                    {
                        return;
                    }
                    var aItem = aShow.ToBackgroundScheduledTransferItem(aStation.ShortName);
                    aItem.DownloadLater = true;
                    aDb.BackgroundScheduleItems.InsertOnSubmit(aItem);
                    aDb.SubmitChanges();
                }
            }
        }

        private static async Task AddItemToDownloadQueue(BackgroundScheduledTransferItem iTransferItem)
        {
            try
            {
                var curBlob = await FetchBlobUri(iTransferItem);
                if (curBlob == null && DateTime.Now.AddHours(-6) > iTransferItem.StopTime)
                {
                    iTransferItem.ErrorMessage = "cloud failure, id = " + iTransferItem.ShowId + ";" + iTransferItem.ErrorMessage;
                    iTransferItem.MovedToMediaLibrary = true; //Todo: split "giveup" and "finished" flags
                }

                if (curBlob != null)
                {
                    StartBlobDownload(iTransferItem, curBlob);
                }
            }
            catch (Exception e)
            {
                iTransferItem.ErrorMessage = e.ToString() + ";" + iTransferItem.ErrorMessage;
            }
        }

        private static void StartBlobDownload(BackgroundScheduledTransferItem iTransferItem, ScheduleBlob curBlob)
        {
            var aFilename = String.Format("/shared/transfers/{0}.mp3", iTransferItem.ShowId);
            var aRequest = CreateTransferRequest(curBlob, aFilename);
            iTransferItem.BackgroundTransferRequestId = aRequest.RequestId;
            iTransferItem.ErrorMessage = "started;" + iTransferItem.ErrorMessage;
            BackgroundTransferService.Add(aRequest);
        }

        private static BackgroundTransferRequest CreateTransferRequest(ScheduleBlob curBlob, string aFilename)
        {
            var aRequest = new BackgroundTransferRequest(
                new Uri(curBlob.RecordedUri, UriKind.Absolute),
                new Uri(aFilename, UriKind.Relative))
            {
                TransferPreferences = TransferPreferences.AllowBattery
            };
            aRequest.TransferStatusChanged += ARequestOnTransferStatusChanged;
            return aRequest;
        }

        private static async Task<ScheduleBlob> FetchBlobUri(BackgroundScheduledTransferItem iTransferItem)
        {
            foreach (var iterBlob in await
                ShowsData.Instance.BlobsTable.
                    Where(blob => blob.Id == iTransferItem.ShowId).
                    Where(blob => blob.RecordedUri != null).
                    ToCollectionAsync())
            {
                if (iterBlob.RecordedUri != null && iterBlob.RecordedUri.Any())
                {
                    return iterBlob;
                }
            }
            return null;
        }

        private static void ARequestOnTransferStatusChanged(object sender, BackgroundTransferEventArgs backgroundTransferEventArgs)
        {
            try
            {
                var aRequest = backgroundTransferEventArgs.Request;
                if (aRequest == null)
                {
                    return;
                }
                if (aRequest.TransferStatus != TransferStatus.Completed)
                {
                    return;
                }
                FinalizeDownload(backgroundTransferEventArgs, aRequest);
            }
            catch (Exception e)
            {
                var aToast = new ShellToast()
                {
                    Title = "Error",
                    Content = e.ToString()
                };
                aToast.Show();
            }

        }

        private static void FinalizeDownload(BackgroundTransferEventArgs backgroundTransferEventArgs,
            BackgroundTransferRequest aRequest)
        {
            using (var db = new BackgroundScheduleDataContext(BackgroundScheduleModel.DefaultDbConnectionString))
            {
                if (!db.DatabaseExists())
                {
                    //Ignore call
                    return;
                }
                try
                {
                    var aScheduleItem =
                        db.BackgroundScheduleItems.FirstOrDefault(item => item.BackgroundTransferRequestId == aRequest.RequestId);
                    if (aScheduleItem == null)
                    {
                        RegisterDetectedOrphan(db, aRequest);
                    }
                    else
                    {
                        MoveDownloadedItemToMediaLibrary(aRequest, aScheduleItem);
                    }
                    db.SubmitChanges(ConflictMode.ContinueOnConflict);
                    BackgroundTransferService.Remove(backgroundTransferEventArgs.Request);
                }
                catch (Exception e)
                {
                    var toast = new ShellToast
                    {
                        Title = "Error recording",
                        Content = e.ToString()
                    };
                    toast.Show();
                    //ignore, nowhere to log this error
                }
            }
        }

        private static void MoveDownloadedItemToMediaLibrary(BackgroundTransferRequest aRequest,
            BackgroundScheduledTransferItem aScheduleItem)
        {
            aScheduleItem.MovedToMediaLibrary = true;
            var aMediaLibrary = new MediaLibrary();
            aMediaLibrary.SaveSong(aRequest.DownloadLocation,
                new SongMetadata
                {
                    AlbumName = CreateAlbumName(aScheduleItem),
                    ArtistName = "ČESKÝ ROZHLAS",
                    Name = CreateSongName(aScheduleItem),
                    GenreName = "nahrávky z rádií"
                }, SaveSongOperation.MoveToLibrary);
        }

        private static string CreateAlbumName(BackgroundScheduledTransferItem iShow)
        {
            return String.Format("{1}'{0:yyyy}", iShow.StartTime, iShow.StationTitle);
        }

        private static string CreateSongName(BackgroundScheduledTransferItem iShow)
        {
            return String.Format("{1} {0:MM/dd HH:mm}", iShow.StartTime, iShow.ShowTitle);
        }

        private static bool RemoveAllCompletedTransferRequests(BackgroundScheduleDataContext iDb)
        {
            var aSubmitRequired = false;
            foreach (var request in BackgroundTransferService.Requests)
            {
                if (request.TransferStatus == TransferStatus.Completed)
                {
                    aSubmitRequired |= RemoveCompletedTransferRequest(iDb, request);
                }
            }
            return aSubmitRequired;
        }

        private static bool RemoveCompletedTransferRequest(BackgroundScheduleDataContext iDb, BackgroundTransferRequest request)
        {
            bool aSubmitRequired = false;
            var aTransferScheduleItem =
                iDb.BackgroundScheduleItems.FirstOrDefault(item => item.BackgroundTransferRequestId == request.RequestId);
            if (aTransferScheduleItem != null)
            {
                if (!aTransferScheduleItem.MovedToMediaLibrary)
                {
                    aTransferScheduleItem.ErrorMessage = "lost on the way :(;" + aTransferScheduleItem.ErrorMessage;
                    aSubmitRequired = true;
                }
            }
            else
            {
                RegisterDetectedOrphan(iDb, request);
                aSubmitRequired = true;
            }
            BackgroundTransferService.Remove(request);
            return aSubmitRequired;
        }

        private static void RegisterDetectedOrphan(BackgroundScheduleDataContext db, BackgroundTransferRequest request)
        {
            db.BackgroundScheduleItems.InsertOnSubmit(new BackgroundScheduledTransferItem()
            {
                ShowTitle = request.DownloadLocation.OriginalString,
                StationTitle = request.RequestId,
                ErrorMessage = "orphan download found",
                StartTime = DateTime.Now,
                StopTime = DateTime.Now,
                DownloadLater = true, //At this moment, it is a big question - if we add another flag "visible in downloads list", or change the filter, so that we do not need to abuse DownloadLater flag
                MovedToMediaLibrary = true
            });
        }

        public static String GetDownloadStatus(BackgroundScheduledTransferItem item)
        {
            if (item.MovedToMediaLibrary)
            {
                return "Finished";
            }
            if (item.BackgroundTransferRequestId == null)
            {
                return "pending";
            }
            var aItemDownloadState = BackgroundTransferService.Find(item.BackgroundTransferRequestId);
            if (aItemDownloadState == null)
            {
                return "lost track";
            }
            else
            {
                return "status " + aItemDownloadState.StatusCode;
            }
        }


        public static bool CloudTransfersTimerIsAlive = false;

        public static void HandleCloudTransfers()
        {
            lock (ShowsData.Instance.BlobsTable)
            {
                if (CloudTransfersTimerIsAlive)
                {
                    return;
                }
                CloudTransfersTimerIsAlive = true;
            }
            ThreadPoolTimer.CreateTimer(
                timer => RefreshAllTransfers(), new TimeSpan(
                    0, 0, 3), timer => CloudTransfersTimerIsAlive = false);
        }
    }
}
